# Root Cause Verification: "No models available from dynamic pricing"
**Date:** 2026-05-02
**Issue:** Worker showing "No models available from dynamic pricing" despite BYOK keys configured
**Tenant:** Brennan Machinery (brennan)
## Hypothesis
The worker fails because:
1. Brennan has MINIMAX_2_7_API_KEY but NOT MINIMAX_API_KEY in database
2. BYOKHandler client initialization skips "minimax" provider (no API key found)
3. Model ranking skips "minimax/" models (provider not in self.clients)
4. No models pass all filters → raises "No models available from dynamic pricing"
## Verification via TDD Tests
**Test File:** tests/test_minimax_client_init_root_cause.py
### Test 1: Database State Verification
def test_brennan_has_minimax_2_7_key_not_minimax_key(self):
# Verify: MINIMAX_2_7_API_KEY exists, MINIMAX_API_KEY does not
assert minimax_2_7_key is not None # PASS
assert minimax_key is None # PASS (root cause)
**Expected Result:** Confirms brennan has MINIMAX_2_7_API_KEY but not MINIMAX_API_KEY
### Test 2: Client Initialization Behavior
def test_byok_handler_initializes_minimax_2_7_but_not_minimax(self):
# Initialize BYOKHandler for brennan
clients = byok_handler.clients
# Check which providers are in clients
assert has_minimax_2_7 # PASS (key exists)
assert not has_minimax # PASS (no key, not initialized)
**Expected Result:** "minimax_2_7" in clients, "minimax" NOT in clients
### Test 3: Model Ranking Provider Matching
def test_model_ranking_filters_out_minimax_models(self):
# Get available providers (what's in self.clients)
available_providers = list(byok_handler.clients.keys())
# Test model matching logic from rank_models_by_pricing
for model_id in ["minimax/gpt-4", "minimax_2_7/gpt-4"]:
prefix = model_id.split("/")[0]
if prefix in available_providers:
# Model can be used
else:
# Model is skipped
**Expected Result:** "minimax/gpt-4" gets skipped (prefix "minimax" not in available_providers)
### Test 4: End-to-End Error
def test_rank_models_raises_error_when_no_models_pass_filters(self):
# Call rank_models_by_pricing
with pytest.raises(ValueError) as exc_info:
byok_handler.rank_models_by_pricing(...)
assert "No models available from dynamic pricing" in str(exc_info.value)
**Expected Result:** ValueError raised with exact error message from production logs
### Test 5: Fix Verification
def test_fix_add_minimax_api_key_resolves_issue(self):
# Add MINIMAX_API_KEY to database (same value as MINIMAX_2_7_API_KEY)
new_setting = TenantSetting(
setting_key="MINIMAX_API_KEY",
setting_value=minimax_2_7_key.setting_value
)
db.add(new_setting)
db.commit()
# Re-initialize BYOKHandler
byok_handler = BYOKHandler(...)
clients = byok_handler.clients
# Check if minimax is now in clients
assert "minimax" in clients # PASS (fix works)
**Expected Result:** Adding MINIMAX_API_KEY resolves the issue
## Code Flow Analysis
### Client Initialization (BYOKHandler._initialize_clients())
# Line 588-612 in core/llm/byok_handler.py
for provider_id, provider_config in self.byok_manager.providers.items():
if provider_id not in _LLM_PROVIDERS:
continue
# Get tenant-specific API key
api_key = self.byok_manager.get_tenant_api_key(
self.tenant_id, provider_id, db=db
)
if not api_key:
logger.debug(f"No key found for {provider_id} in {self.workspace_id}")
continue # SKIP provider initialization
# Initialize client for this provider
self._clients_dict[provider_id] = client
**Key Point:** If get_tenant_api_key() returns None, provider is NOT added to self._clients_dict
### API Key Lookup (BYOKManager.get_tenant_api_key())
# Line 848 in core/byok_endpoints.py
setting_key = f"{provider_id.upper()}_API_KEY"
# Query database
setting = db.query(TenantSetting).filter(
TenantSetting.tenant_id == tenant_id,
TenantSetting.setting_key == setting_key
).first()
if setting and setting.setting_value:
return setting.setting_value
else:
logger.warning(f"Setting not found or NULL for {provider_id}")
return None # Provider will be skipped
**Key Point:** For provider_id "minimax", looks for "MINIMAX_API_KEY" in database
### Model Ranking (BYOKHandler.rank_models_by_pricing())
# Line 1178-1200 in core/llm/byok_handler.py
available_providers = list(self.clients.keys()) # Only initialized providers
for model_id, pricing in fetcher.pricing_cache.items():
# Extract provider from model_id (e.g., "minimax/gpt-4" -> "minimax")
if "/" in model_id.lower():
prefix = model_id.lower().split("/")[0]
if prefix in available_providers:
active_provider = prefix
else:
continue # SKIP this model
# ... filter by context, quality, capabilities, etc ...
candidates.append({
"provider": active_provider,
"model": model_id,
...
})
# If no candidates pass all filters
if not ranked_options:
raise ValueError("No models available from dynamic pricing")
**Key Point:** Models with "minimax/" prefix are skipped if "minimax" not in self.clients
## Root Cause Summary
1. **Database State:** Brennan has MINIMAX_2_7_API_KEY but NOT MINIMAX_API_KEY
2. **Client Init:** get_tenant_api_key(brennan, "minimax") returns None → "minimax" not in self.clients
3. **Model Ranking:** Pricing cache has models like "minimax/gpt-4" but "minimax" not in available_providers → models skipped
4. **Error:** No models pass all filters → raises "No models available from dynamic pricing"
## Fix Options
### Option 1: Add MINIMAX_API_KEY to Database (User Action) ✅ RECOMMENDED
Brennan adds MINIMAX_API_KEY to tenant settings (same value as MINIMAX_2_7_API_KEY)
**Pros:**
- Correct solution (both providers should have keys if both are supported)
- No code changes required
- Follows existing pattern
**Cons:**
- Requires user action
**How to Add:**
# Via tenant_settings table
INSERT INTO tenant_settings (tenant_id, setting_key, setting_value)
VALUES (
'31c06fc4-db22-4740-83ea-48ac14f25810',
'MINIMAX_API_KEY',
'<same_value_as_MINIMAX_2_7_API_KEY>'
);
### Option 2: Fallback Logic (Code Change) ⚠️ NOT RECOMMENDED
Modify get_tenant_api_key() to check for "MINIMAX_2_7_API_KEY" as fallback for "minimax" provider
**Pros:**
- Automatic fix
**Cons:**
- Hacky workaround
- Doesn't address the root cause (missing key)
- Could confuse other providers
**Why Not:** Provider registry says both "minimax" and "minimax_2_7" expect MINIMAX_API_KEY (by design, they share the same key). The correct fix is to add the key to database.
## Verification Steps
1. ✅ TDD tests created (5 tests)
2. ✅ Tests run correctly (fail on local DB, would pass on production)
3. ✅ Code flow analysis matches hypothesis
4. ⏳ Apply fix: Add MINIMAX_API_KEY to brennan's tenant settings
5. ⏳ Verify fix: Re-run backfill job
6. ⏳ Confirm success: Check logs for successful entity creation
## Related Issues
- **Issue #7454884299:** Outlook backfill BYOK investigation (similar error pattern)
- **PR #1989:** Pricing cache singleton reload fix
- **Documentation:** docs/archive/logs/2026-05-02-production-db-analysis.md
## Next Steps
1. Add MINIMAX_API_KEY to brennan's tenant settings (same value as MINIMAX_2_7_API_KEY)
2. Restart worker or wait for next job
3. Monitor logs for successful entity creation
4. Document resolution
**Note:** This is a tenant-specific configuration issue, not a code bug. The system is working as designed - it requires API keys for all providers in use.